{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Breaking Hardware ECC on CW305 FPGA, part 5\n",
    "\n",
    "This builds on CW305_ECC parts 1, 2, 3 and 4 notebooks; be sure to digest them before starting this one.\n",
    "\n",
    "In this notebook, we look at what TVLA can tell us about our target's leakage.\n",
    "\n",
    "The tutorial was developed with a CW-Pro with the CW305 100t target FPGA; the observations made in the attack's development should be accurate if you're using the same, but other combinations of CW-Pro / CW-Lite / CW-Husky / CW305 100t / 35t / CW312T-A35 may behave somewhat differently (some definitely do!).\n",
    "\n",
    "Unlike the previous parts of this series tutorials, there are no pre-recorded traces available here -- they would simply be too large to feasibly share. Some of the results shown here can be seen in the [Ark of the ECC eprint paper](https://eprint.iacr.org/2021/1520.pdf)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "See CW305_ECC_part1.ipynb for explanations which are not repeated here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#PLATFORM = 'CWLITE'\n",
    "#PLATFORM = 'CWPRO'\n",
    "PLATFORM = 'CWHUSKY'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#TARGET_PLATFORM = 'CW305_100t'\n",
    "#TARGET_PLATFORM = 'CW305_35t'\n",
    "TARGET_PLATFORM = 'CW312T_A35'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "TRACES = 'HARDWARE'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import chipwhisperer as cw\n",
    "\n",
    "scope = cw.scope()\n",
    "scope.default_setup()\n",
    "if TARGET_PLATFORM == 'CW312T_A35':\n",
    "    scope.io.hs2 = 'clkgen'\n",
    "    fpga_id = 'cw312t_a35'\n",
    "    platform = 'ss2'\n",
    "else:\n",
    "    scope.io.hs2 = \"disabled\"\n",
    "    platform = 'cw305'\n",
    "    if TARGET_PLATFORM == 'CW305_100t':\n",
    "        fpga_id = '100t'\n",
    "    elif TARGET_PLATFORM == 'CW305_35t':\n",
    "        fpga_id = '35t'\n",
    "\n",
    "target = cw.target(scope, cw.targets.CW305_ECC, force=False, fpga_id=fpga_id, platform=platform)\n",
    "\n",
    "# ensure ADC is locked:\n",
    "scope.clock.reset_adc()\n",
    "assert (scope.clock.adc_locked), \"ADC failed to lock\"\n",
    "\n",
    "%run \"CW305_ECC_setup.ipynb\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TVLA\n",
    "\n",
    "We begin by conducting a TVLA test on the original core.\n",
    "\n",
    "Normally, TVLA is conducted on the full target operation. But here our target operation is very long, and TVLA requires lots of traces; given that we already know that except for the leading 1, leakage for each bit of $k$ appears identical, we'll only capture the first few bits.\n",
    "\n",
    "(Capture the full operation if you wish, but be mindful that it's easy to run into \"out of memory\" errors. If this happens, reduce the number of traces, or capture and save them to disk in chunks. The TVLA calculation itself will also take a lot more time on the full traces.)\n",
    "\n",
    "Furthermore, in the interest of time, we'll only capture 2000 traces. Feel free to capture more; 10000 per group is a common number, as per \"Security Level 3\" of ISO 17825, but it does not make much difference to our results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "change_bitfile('original')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tvlattest_ecc as TVLA\n",
    "ktp = TVLA.TVLATTest_ECC(target.curve)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "set_adc(samples=int(cycles[9]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_tvla_traces(N=2000, group=2):\n",
    "    traces = []\n",
    "    ktp.init(traces=N, groups=[0,group])\n",
    "    for i in trange(N, desc='Capturing traces'):\n",
    "        k, P, group = ktp.next()\n",
    "        ret = target.capture_trace(scope, Px=P.x, Py=P.y, k=k)\n",
    "        if not ret:\n",
    "            print(\"Failed capture\")\n",
    "            continue\n",
    "        ret.textout['group'] = group\n",
    "        traces.append(ret)\n",
    "    return traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By specifying `group=2`, we will capture traces belonging to groups 0 and 2, which are defined in our `TVLATTest_ECC` class.\n",
    "\n",
    "Group 0 holds $k$ and the base point $P$ fixed for each capture.\n",
    "\n",
    "Group 2 hold $P$ fixed, and randomizes $k$ for each capture.\n",
    "\n",
    "The TVLA test looks at statistical differences between these two groups, therefore this will show the effect of varying $k$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tvla_traces = get_tvla_traces(N=2000, group=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def _group_traces(traces, group1, group2, start, stop, stat):\n",
    "    \"\"\"Split a set of traces into a and b lists of grouped traces.\n",
    "    Meant to be called by calc_tvla().\n",
    "    Args:\n",
    "        traces: list of traces\n",
    "        groups (int): number of groups contained in the traces\n",
    "        start (int): start index for TVLA stats\n",
    "        stop (int): stop index for TVLA stats; None = to the end\n",
    "        stat (string): data to return:\n",
    "            \"full\": full measured data\n",
    "            \"avgbit\": average bit\n",
    "    Returns:\n",
    "        grouped_data: 2-element list of lists; 1st list is requested data for group1,\n",
    "            2nd list is requested data for group2.\n",
    "    \"\"\"\n",
    "    grouped_data = [[], []]\n",
    "    for trace in traces:\n",
    "        if stat == 'full':\n",
    "            data = trace.wave[start:stop]\n",
    "        elif stat == 'avgbit':\n",
    "            data = trace.textout['avgbit']\n",
    "        else:\n",
    "            raise ValueError()\n",
    "        if trace.textout['group'] == group1:\n",
    "            grouped_data[0].append(data)\n",
    "        elif trace.textout['group'] == group2:\n",
    "            grouped_data[1].append(data)\n",
    "            \n",
    "    return grouped_data\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calc_tvla(traces, group1, group2, start=0, stop=None, stat='full', verbose=True):\n",
    "    \"\"\"Calculate TVLA data using power (default) or time measurements.\n",
    "    Args:\n",
    "        group1 (int): first group to use in computing TVLA\n",
    "        group2 (int): second group to use in computing TVLA\n",
    "        start (int): start index for TVLA stats (N/A when stat='time')\n",
    "        stop (int): stop index for TVLA stats; None = to the end (N/A when stat='time')\n",
    "        stat (string): compute TVLA using which data:\n",
    "            \"full\": measured power data\n",
    "            \"avgbit\": average bit\n",
    "    Returns:\n",
    "        ttrace_a, ttrace_b: TVLA data for first and second half of traces\n",
    "    \"\"\"\n",
    "    import scipy.stats\n",
    "    import time\n",
    "    start_time = time.time()\n",
    "    grouped_data_1 = []\n",
    "    grouped_data_2 = []\n",
    "    grouped_data_1, grouped_data_2 = _group_traces(traces, group1, group2, start, stop, stat)\n",
    "    \n",
    "    if verbose:\n",
    "        print(\"Found %d data points for group %d.\" % (len(grouped_data_1), group1))\n",
    "        print(\"Found %d data points for group %d.\" % (len(grouped_data_2), group2))\n",
    "    \n",
    "    # then split each into two halves\n",
    "    grouped_data_a = [[], []]\n",
    "    grouped_data_b = [[], []]\n",
    "\n",
    "    half = len(grouped_data_1)//2\n",
    "    grouped_data_a[0] = grouped_data_1[half:]\n",
    "    grouped_data_b[0] = grouped_data_1[:half]\n",
    "    grouped_data_a[1] = grouped_data_2[half:]\n",
    "    grouped_data_b[1] = grouped_data_2[:half]\n",
    "    \n",
    "    if verbose:\n",
    "        print(\"Calculating TVLA... \", end='')\n",
    "    ttrace_a = scipy.stats.ttest_ind(grouped_data_a[0], grouped_data_a[1], axis=0, equal_var=False)[0]\n",
    "    if verbose:\n",
    "        print('group A done... ', end='')\n",
    "    ttrace_b = scipy.stats.ttest_ind(grouped_data_b[0], grouped_data_b[1], axis=0, equal_var=False)[0]\n",
    "    if verbose:\n",
    "        print('group B done.')\n",
    "    elapsed_time = time.time() - start_time\n",
    "    if verbose:\n",
    "        print('Elapsed time: %d seconds.' % elapsed_time)\n",
    "    return ttrace_a, ttrace_b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tta, ttb = calc_tvla(tvla_traces, 0, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.resources import INLINE\n",
    "from bokeh.io import output_notebook\n",
    "from bokeh.models import Span, Legend, LegendItem\n",
    "\n",
    "output_notebook(INLINE)\n",
    "\n",
    "s = figure(width=1300, height=600, x_axis_label='clock cycle')\n",
    "\n",
    "xrange = list(range(len(tta)))\n",
    "\n",
    "T = s.line(xrange, (tta), line_color='red')\n",
    "T = s.line(xrange, (ttb), line_color='blue')\n",
    "\n",
    "for c in cycles[:9]:\n",
    "    s.renderers.extend([Span(location=c, dimension='height', line_color='black', line_width=1, line_dash='dashed')])\n",
    "\n",
    "for p in [6, 4202]:\n",
    "    for c in cycles[:9]:\n",
    "        s.renderers.extend([Span(location=c+p, dimension='height', line_color='red', line_width=1, line_dash='dashed')])\n",
    "\n",
    "s.renderers.extend([Span(location=-4.5, dimension='width', line_color='green', line_width=1, line_dash='dotted')])\n",
    "s.renderers.extend([Span(location=+4.5, dimension='width', line_color='green', line_width=1, line_dash='dotted')])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The raw TVLA plot above is annotated with black dashed lines which indicate the start time of each bit, and red dashed lines which indicate the DoM markers of our attack.\n",
    "\n",
    "Finally, the green dotted lines indicate the TVLA pass/fail thresholds.\n",
    "\n",
    "We make the following observations:\n",
    "1. There are no failures during the first bit, as expected.\n",
    "2. The first failures coincides *exactly* with our cycles 6-7 DoM markers.\n",
    "3. Failures also coincide with our cycles 4202-4203 markers.\n",
    "4. After the first bit, numerous large failures abound throughout the subsequent bits.\n",
    "\n",
    "Let's quantify the last point:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "threshold = 4.5\n",
    "for b in range(9):\n",
    "    fails = 0\n",
    "    for cycle in range(4204):\n",
    "        if (abs(tta[cycles[b] + cycle]) > 4.5) and (abs(ttb[cycles[b] + cycle]) > 4.5):\n",
    "            fails += 1\n",
    "    print('Bit %d: %4d failures (%3d percent of samples)' % (b, fails, int(fails/4204*100)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We find that a **huge** number of samples are failing for bits 1 onwards; around 60% for bit 1, then diminishing on subsequent bits to stabilize around 25% (these numbers vary across the different FPGA targets but should follow the same pattern).\n",
    "\n",
    "So while our DoM markers are present in the TVLA failure set, they appear to be very much lost in the noise.\n",
    "\n",
    "To visualize that, let's overlay the TVLA results with our original \"average of zeros vs ones\" plot which was used to identify the markers for our attack:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "k = 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000\n",
    "avg_trace = get_traces(1, k, full=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "samples = 4204\n",
    "trace = avg_trace[0]\n",
    "avg_ones = np.zeros(samples)\n",
    "for start in cycles[1:128]:\n",
    "    avg_ones += trace.wave[start:start+samples]\n",
    "avg_ones /= 128\n",
    "\n",
    "avg_zeros = np.zeros(samples)\n",
    "for start in cycles[128:256]:\n",
    "    avg_zeros += trace.wave[start:start+samples]\n",
    "avg_zeros /= 128"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.resources import INLINE\n",
    "from bokeh.io import output_notebook\n",
    "from bokeh.models import Span, Legend, LegendItem\n",
    "\n",
    "output_notebook(INLINE)\n",
    "\n",
    "s = figure(width=1300, height=600, x_axis_label='clock cycle')\n",
    "\n",
    "xrange = list(range(len(avg_zeros)))\n",
    "ratio = max(abs(tta)) / max(abs(avg_ones-avg_zeros))\n",
    "\n",
    "T = s.line(xrange, (abs(tta[cycles[7]:cycles[8]])), line_color='red')\n",
    "#T = s.line(xrange, (ttb[cycles[7]:cycles[8]]), line_color='blue')\n",
    "\n",
    "A = s.line(xrange, ratio*abs(avg_ones-avg_zeros), line_color='blue')\n",
    "\n",
    "# add legend:\n",
    "legend = Legend(items=[\n",
    "    LegendItem(label='D (scaled)', renderers=[A]),\n",
    "    LegendItem(label='TVLA result', renderers=[T]),\n",
    "])\n",
    "s.add_layout(legend)\n",
    "s.legend.label_text_font_size='16pt'\n",
    "\n",
    "s.xaxis.axis_label_text_font_size = '20pt'\n",
    "s.yaxis.axis_label_text_font_size = '20pt'\n",
    "s.xaxis.major_label_text_font_size = '14pt'\n",
    "s.yaxis.major_label_text_font_size = '14pt'\n",
    "s.title.text_font_size = '20pt'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's visualize what happens if we collect our attack markers from the TVLA failures (instead of from the DoM results):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "k = 0x0000ffffffffff000000000000ffff00aaaa0000cccc00001111000033330000\n",
    "traces = get_traces(30, k, full=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if TARGET_PLATFORM == 'CW312T_A35':\n",
    "    dom_poi = [4202, -4203, 7, -8]\n",
    "else:\n",
    "    dom_poi = [4202, -4203, -6, 7]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def update_corrected_plot(no_traces, tvla_threshold):\n",
    "    SSC.data_source.data['y'] = get_sums(traces[:no_traces], dom_poi)\n",
    "    \n",
    "    tvla_poi = list(np.where(abs(tta[cycles[7]:cycles[8]]) > tvla_threshold)[0])\n",
    "    SSCtvla.data_source.data['y'] = get_sums(traces[:no_traces], tvla_poi)\n",
    "\n",
    "    push_notebook()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ipywidgets import interact, Layout\n",
    "from bokeh.io import push_notebook, output_notebook\n",
    "\n",
    "SC = figure(width=1200, x_axis_label='k bit index', y_axis_label='D')\n",
    "\n",
    "xrange = list(range(len(cycles)-0))\n",
    "dom_sums = get_sums(traces, dom_poi)\n",
    "SSC = SC.line(xrange, dom_sums, line_color='blue')\n",
    "\n",
    "tvla_threshold = 20\n",
    "tvla_poi = list(np.where(abs(tta[cycles[7]:cycles[8]]) > tvla_threshold)[0])\n",
    "tvla_sums = get_sums(traces, tvla_poi)\n",
    "SSCtvla = SC.line(xrange, tvla_sums, line_color='red')\n",
    "\n",
    "SC.xaxis.axis_label_text_font_size = '20pt'\n",
    "SC.yaxis.axis_label_text_font_size = '20pt'\n",
    "SC.xaxis.major_label_text_font_size = '14pt'\n",
    "SC.yaxis.major_label_text_font_size = '14pt'\n",
    "SC.title.text_font_size = '20pt'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(SC, notebook_handle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "interact(update_corrected_plot, no_traces=(1, len(traces)), tvla_threshold=(4, 40))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you play with the `no_traces` and `tvla_threshold` knobs, it should become apparent that using markers extracted from TVLA failures is an **excellent** distinguisher for the leading 1 of $k$, but nothing else.\n",
    "\n",
    "This makes sense: we can infer that the large number of TVLA failures point to the leakage caused by the leading 1 (and our work on attempt #4 in part 4 of this series supports that).\n",
    "\n",
    "(This also explains why the number of TVLA failures is highest for bit 1 and then decreases, as the leading 1 becomes less and less likely to occur for each subsequent bit.)\n",
    "\n",
    "To wrap up, let's repeat this exercise with the bitfile from attempt #4:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Attempt #4 revisited"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "change_bitfile('attempt4')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "set_adc(samples=int(cycles[9]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tvla_traces4 = get_tvla_traces(N=2000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tta4, ttb4 = calc_tvla(tvla_traces4, 0, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.resources import INLINE\n",
    "from bokeh.io import output_notebook\n",
    "from bokeh.models import Span, Legend, LegendItem\n",
    "\n",
    "output_notebook(INLINE)\n",
    "\n",
    "s4 = figure(width=1300, height=600, x_axis_label='clock cycle')\n",
    "\n",
    "xrange = list(range(len(tta4)))\n",
    "\n",
    "T4 = s4.line(xrange, (tta4), line_color='red')\n",
    "T4 = s4.line(xrange, (ttb4), line_color='blue')\n",
    "\n",
    "for c in cycles[:9]:\n",
    "    s4.renderers.extend([Span(location=c, dimension='height', line_color='black', line_width=1, line_dash='dashed')])\n",
    "\n",
    "for p in [6, 4202]:\n",
    "    for c in cycles[:9]:\n",
    "        s4.renderers.extend([Span(location=c+p, dimension='height', line_color='red', line_width=1, line_dash='dashed')])\n",
    "\n",
    "s4.renderers.extend([Span(location=-4.5, dimension='width', line_color='green', line_width=1, line_dash='dotted')])\n",
    "s4.renderers.extend([Span(location=+4.5, dimension='width', line_color='green', line_width=1, line_dash='dotted')])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(s4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "threshold = 4.5\n",
    "for b in range(9):\n",
    "    fails = 0\n",
    "    for cycle in range(4204):\n",
    "        if (abs(tta4[cycles[b] + cycle]) > 4.5) and (abs(ttb4[cycles[b] + cycle]) > 4.5):\n",
    "            fails += 1\n",
    "    print('Bit %d: %4d failures (%3d percent of samples)' % (b, fails, int(fails/4204*100)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So far, this looks the same as it did with the original bitfile."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "k = 0x0000ffffffffff000000000000ffff00aaaa0000cccc00001111000033330000\n",
    "traces4 = get_traces(20, k, full=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dom_poi4 = [4201, -4202, -6, 7]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def update_corrected_plot4(no_traces, tvla_threshold):\n",
    "    SSC4.data_source.data['y'] = get_sums(traces4[:no_traces], dom_poi4)\n",
    "    \n",
    "    tvla_poi = list(np.where(abs(tta[cycles[7]:cycles[8]]) > tvla_threshold)[0])\n",
    "    SSCtvla4.data_source.data['y'] = get_sums(traces4[:no_traces], tvla_poi)\n",
    "\n",
    "    push_notebook()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ipywidgets import interact, Layout\n",
    "from bokeh.io import push_notebook, output_notebook\n",
    "\n",
    "SC4 = figure(width=1200, x_axis_label='k bit index', y_axis_label='D')\n",
    "\n",
    "xrange = list(range(len(cycles)))\n",
    "dom_sums4 = get_sums(traces4, dom_poi4)\n",
    "SSC4 = SC4.line(xrange, dom_sums4, line_color='blue')\n",
    "\n",
    "tvla_threshold4 = 20\n",
    "tvla_poi4 = list(np.where(abs(tta4[cycles[7]:cycles[8]]) > tvla_threshold4)[0])\n",
    "tvla_sums4 = get_sums(traces4, tvla_poi4)\n",
    "SSCtvla4 = SC4.line(xrange, tvla_sums4, line_color='red')\n",
    "\n",
    "SC4.xaxis.axis_label_text_font_size = '20pt'\n",
    "SC4.yaxis.axis_label_text_font_size = '20pt'\n",
    "SC4.xaxis.major_label_text_font_size = '14pt'\n",
    "SC4.yaxis.major_label_text_font_size = '14pt'\n",
    "SC4.title.text_font_size = '20pt'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(SC4, notebook_handle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "interact(update_corrected_plot4, no_traces=(1, len(traces4)), tvla_threshold=(4, 40))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you may have expected, the results are similar to what we saw with the original bitfile: using markers extracted from TVLA failures is an **excellent** distinguisher for the leading 1 and leading 0 of $k$, but nothing else.\n",
    "\n",
    "Now, you may think that TVLA could be used to reveal the specific markers for bits that come after the leading 1; what if we define our test group (i.e. group 2, defined at the start of this notebook) to have a randomized $k$ where the leading 1 is always at the same position?\n",
    "\n",
    "Try it! Re-run with the group set to 15. You'll see that this modified TVLA test gives us essentially the very same markers as the original TVLA test, except that it is now the *second* leading one that is leaked. So, we are no further ahead. The reason for this should be obvious after some thought.\n",
    "\n",
    "This highlights the limitations of TVLA in the context of our particular target and attack."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Conclusion\n",
    "\n",
    "This 5-part series of demos has covered a lot of ground for hardware-based ECC attacks and defenses. Hopefully you found it useful!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
